///
/// ArmyBodger 3
/// (c)2010 richyp
///
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Scripting;
using IronPython.Runtime;

namespace ArmyBodger.Core {
    /// <summary>
    /// Class that acts as a gateway between the ArmyBodger C# core and
    /// the IronPython script engine
    /// </summary>
    public class ScriptCore {
        /// <summary>
        /// Default Constuctor
        /// </summary>
        private ScriptCore() {
            this.engine = IronPython.Hosting.Python.CreateEngine();                        
            this.scope = this.engine.CreateScope();
            this.AddVariable("Core", this);
            this.AddVariable("py", this.engine);
            this.Execute("import sys");
        }
        /// <summary>
        /// The Python runtime instance
        /// </summary>
        protected Microsoft.Scripting.Hosting.ScriptEngine engine;
        /// <summary>
        /// The scope/instance used. This is so that values are preserved
        /// rather than using a new scope each time and losing values
        /// </summary>
        protected Microsoft.Scripting.Hosting.ScriptScope scope;
        /// <summary>
        /// The singleton instance of this object
        /// </summary>
        private static ScriptCore instance = null;
        /// <summary>
        /// Get the singleton instance of the scriptcore
        /// </summary>
        public static ScriptCore Instance {
            get {
                if (instance == null) {
                    instance = new ScriptCore();
                }
                return instance;
            }
        }
        /// <summary>
        /// Add a reference to avariable to the scriptcore so that it can be referred to
        /// in python
        /// </summary>
        /// <param name="name">The name to give the reference</param>
        /// <param name="value">The object referred to</param>
        public void AddVariable(string name, object value) {
            this.scope.SetVariable(name, value);
        }
        /// <summary>
        /// Load a .py file and execute it
        /// </summary>
        /// <param name="file">The file to run</param>
        public void LoadFile(string file) {
            try
            {
                this.engine.ExecuteFile(file, this.scope);
            }
            catch (Exception ex)
            {
                this.ShowExecptionDialog(file, ex);
            }
        }
        /// <summary>
        /// Execute the specified string via the script engine
        /// </summary>
        /// <param name="script">The string to execute</param>
        public dynamic Execute(string script) {
            try {
                return this.engine.Execute(script, this.scope);
            } catch (Exception ex) {
                this.ShowExecptionDialog(script, ex);
            }
            return null;
        }
        /// <summary>
        /// Get a reference to the builtin script engine
        /// </summary>
        /// <returns></returns>
        public Microsoft.Scripting.Hosting.ScriptEngine GetEngine() {
            return this.engine;
        }
        /// <summary>
        /// Redirect the Python Streams
        /// </summary>
        /// <param name="output">The stream to use</param>
        public void SetOutputStream(System.IO.Stream output) {
            object sysObject = this.scope.GetVariable("sys");
            Microsoft.Scripting.Hosting.ScriptRuntime r = this.scope.Engine.Runtime;
            r.IO.SetOutput(output, new System.IO.StreamWriter(output));
            //this.engine.Operations.SetMember(sysObject, "stdout", output);
            //this.engine.Operations.SetMember(sysObject, "stderr", output);
        }

        /// <summary>
        /// Displays an error dialog when passed an execption from a python script
        /// </summary>
        /// <param name="ex"></param>
        public void ShowExecptionDialog(string script, Exception ex)
        {
            // Show the error in a dialogbox owned by the main bodger form
            if (ex is SyntaxErrorException)
            {
                SyntaxErrorException synEx = (SyntaxErrorException)ex;
                this.Execute("MessageBox.Show(\"Python error in function \\\"" + script + "\\\", Line " + synEx.Line + ": " + ex.Message + "\")");
            }
            else
                this.Execute("MessageBox.Show(\"Python error in function \\\"" + script + "\\\": " + ex.Message + "\")");
        }
    }
}
